home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / GRAPHICS / VGATUT1.ZIP / TUT1.TXT next >
Encoding:
Text File  |  1995-08-05  |  15.6 KB  |  470 lines

  1.                   ================
  2.                   = VGA Tutorial =
  3.                   =      By      =
  4.                   = Barny Mercer =
  5.                   ================
  6.                   Part I -  PIXELS
  7.                   ----------------
  8.  
  9. Firstly, *many* thanks to Richard Griffiths for porting the code in this
  10. tutorial to Pascal.
  11.  
  12. Hi and welcome to the first issue of my (hopefully) weekly tutorial on
  13. programming graphics for the PC.
  14.  
  15. This tutorial deals with writing pixels to the screen and looks at a couple
  16. of different methods of doing so.
  17.  
  18. Before we start :
  19.  
  20. -----------------------------------------------------------------------------
  21.     DISCLAIMER
  22.  
  23.     The code for this tutorial, the compiled .EXEs and any ascociated text
  24.     are freely distributable on the conditions that the contents of the 
  25.     original .ZIP file are distributed as one and that no changes are made
  26.     to any of the data or information contained within.
  27.     The author expresses no warranty implied or otherwise as to the
  28.     suitabilty of this software for execution on any machine other than
  29.     the system on which it was developed (although there should be no
  30.     problems).
  31.     The author also takes no responsibilty for damage resulting from the
  32.     use of information, code or otherwise, obtained from this tutorial 
  33.     (again, I'd be surprised if such a thing did happen).
  34.     Finally you should _NOT_ have paid anything for this tutorial.  
  35.     If you parted with any money (other than a reasonalbe price for a 
  36.     disk) then complain and get your money back.  Please let me know too. 
  37.     This tutorial is FREE.  Please do not abuse this.
  38. -----------------------------------------------------------------------------
  39.  
  40. Sorry about all that, but unfortunately we live in a world where such things
  41. are a necessity.
  42.  
  43. Right!
  44.  
  45. There are a number video modes available on the VGA graphics card.  Each one
  46. is identified by a value which is passes to the BIOS interrupt 10h 
  47. required to call it.  If this is nonsense to you then don't worry.  
  48. All will become clear...... (I hope)
  49.  
  50. During this tutorial we will be concentrating on one particular video mode.
  51.  
  52. Mode 13h
  53. --------
  54.  
  55. Mode 13h is 320 pixels wide by 200 pixels and capable of displaying 256 
  56. colours on screen at one time. We therefore have 64000 pixels to play with. 
  57.  
  58. Each pixel on screen is represented by 1 byte of video memory.  Graphics are
  59. basically created by setting the value of these bytes.  A byte can store
  60. values in the range of 0 to 255, that's 256 different values available.
  61. Each one of these values is linked to a colour index which informs the VGA
  62. card which colour is wanted on screen.
  63.  
  64. Easy?  Absolutely.
  65.  
  66. --------------------------
  67. So how do I place a pixel?
  68.  
  69. In order to demonstrate we'll write a short program to select the appropriate
  70. video mode, place some pixels and return to text mode.
  71.  
  72. That specification is already nicely broken into simple pieces so we will
  73. expand on each one.
  74.  
  75.     Select mode 13h
  76.     ---------------
  77.  
  78.     The easiest and quickest way to do this is with assembly, but I will
  79.     explain the principle involved first.
  80.  
  81.     The BIOS interrupt number for changing the VGA mode is 10h (that's 
  82.     16 decimal, for anyone who's unfamiliar with hexadecimal).
  83.     There are lots of other interrupt numbers for dealing with the VGA 
  84.     but we will concentrate on 10h for now.
  85.  
  86.     To switch to mode 13h we need to tell the ROM BIOS two things :
  87.     
  88.     1. Which mode we want (13h in this case)
  89.     2. And what to do with that number (A video operation)
  90.  
  91.     So the code looks like this :
  92.  
  93.     // -----------------------------------
  94.     // Mode 13h Selection - Barny Mercer
  95.     // -----------------------------------
  96.  
  97.     void main(void)
  98.     {
  99.         _asm    // tell the compiler that we are writing assembly code
  100.         {
  101.             mov ax, 13h      // store 13h in AX
  102.             int 10h          // call interrupt
  103.         }
  104.     }
  105.  
  106.     Note the use of the AX register.  The INT instruction passes the value
  107.     stored in AX to the VGA card and this is what sets the video mode.
  108.  
  109.     By placing different values in AX we can access different video modes.
  110.  
  111.     If you are not familliar with assembly, then do not worry.  You can 
  112.     still use the code, and learn as you go along.  If you need a quick 
  113.     assembly tutorial then drop me a line.
  114.  
  115.     That's it!  Erm... well not quite.  This program will switch to
  116.     mode 13h but leave you there when it termiates.  We need a way of
  117.     returning to text mode when we are done.  Easy...!
  118.  
  119.     To return to text mode we just replace 13h in the above code with
  120.     the appropriate video mode.  25 line text mode has the value 03h, so..
  121.  
  122.     // -----------------------------------
  123.     // Text Mode Selection - Barny Mercer
  124.     // -----------------------------------
  125.  
  126.     void main(void)
  127.     {
  128.         _asm
  129.         {
  130.             mov ax, 03h      // store 03h in AX
  131.             int 10h          // call interrupt
  132.         }
  133.     }
  134.  
  135.     There...  2 programs for switching video modes.
  136.  
  137.     'Errmmm...', I hear you say, 'They're not really very useful are
  138.     they?!'.  Well, no, but they illustrate what we need to know.
  139.  
  140.     To make them more useful we will turn them into a single function
  141.     which can be called whenever you need to switch video modes.
  142.  
  143.     Take a look at this code:
  144.  
  145.     // ----------------------------------------
  146.     // Mode Selection Function - Barny Mercer
  147.     // ----------------------------------------
  148.  
  149.     // define preprocessor constants
  150.     #define VID_320x200 0x0013              // 0x informs the compiler
  151.     #define VID_TEXT    0x0003              // that the value is in hex
  152.  
  153.     void SetVideo( int Mode )
  154.     {
  155.         _asm
  156.         {
  157.             mov ax, [Mode]   // store parameter in AX
  158.             int 10h          // call interrupt
  159.         }
  160.     }
  161.  
  162.     Now when you want to switch video modes in your programs you do so
  163.     by calling the function as follows:
  164.  
  165.     SetVideo( VID_320x200 );                // select 320x200x256 - 13h
  166.     or
  167.     SetVideo( VID_TEXT );                   // select text mode
  168.  
  169.     Easy peasy!  (cheesy expression number 1.)
  170.  
  171. Well... we've managed to cover 2 of the three steps that we outlined above, so
  172. without further ado, let's move on to the most interesting bit.
  173.  
  174.     Placing Pixels
  175.     --------------
  176.  
  177.     There are essentially two ways that this can be done.
  178.  
  179.         Interrupts
  180.  
  181.             By using a smilar technique to the one above we can
  182.             send a pixel to video memory via the ROM BIOS.
  183.             Using BIOS however is a very slow way of doing things.
  184.  
  185.         Direct Memory Access (DMA)
  186.  
  187.             A faster method is to avoid the BIOS completely and
  188.             write our value directly to memory.
  189.  
  190.     I'll talk first about the DMA method.
  191.  
  192.     In order to place a pixel we need to know two things.  The location 
  193.     of the pixel and the colour.  Obvious?  Of course it is.
  194.  
  195.     Now comes the tricky bit.  When you look at your monitor what do you
  196.     see?  200 rows of 320 pixels.  Fair enough, but that is not the way
  197.     in which the screen is represented as data.
  198.  
  199.     Video memory is, for most purposes, treated just like ordinary memory.
  200.     A specific point in memory is loacted by two values. Both of these 
  201.     are 16 bit values (range 0 - 65535).  The first (SEGMENT) points to 
  202.     a 64k chunk of memory and the second (OFFSET) points to a specific 
  203.     byte inside that chunk.  Video memory for the VGA has the segment 
  204.     0xA000.  Therefore we say that the address of the first byte of video
  205.     memory is A000:0000 with the next byte appearing at (logically) 
  206.     A000:0001.  Remember that these addresses are in hexadecimal.
  207.  
  208.     (A hexadecimal/binary/decemal explanation is not provided here, but
  209.      if you need one then feel free to contact me and I'll be happy to
  210.      oblige)
  211.  
  212.     The video memory offset therefore extends from 0000 (0) to 
  213.     FFFF (65535) (although only the first FA00 (64000 = 320x200) are
  214.     visible.  
  215.  
  216.     To place a pixel on screen, we stuff the appropriate value in the 
  217.     appropriate memory location!  Hoorah!! <wave banners> <sing songs>
  218.     <generally celebrate> <gradually realise that there must be a catch>
  219.     <stop singing> <wait for bad news>
  220.  
  221.     While your image occurs on a 320x200 grid using a coordinate system, 
  222.     we are actually placing values in memory by specifying only 1 value.
  223.     By modifying the offset we can point to a specific byte.
  224.     Actually, it's not so difficult.  A very simple formula allows us to 
  225.     locate the correct offset based on our (X, Y) values.
  226.  
  227.     offset = (Y * 320) + x
  228.  
  229.     Therefore, a point (55,100) has the offset
  230.     ( ( 100 * 320 ) + 55 ) = 32055 (0x7D37)
  231.     
  232.     To place a white pixel at point (55,100) we would place the value
  233.     0x0F (15 dec) at byte 0x7D37.  The address looks like this A000:7D37
  234.  
  235.     All okay so far?  Good.  I'm glad to hear it.  What?!  It's not?
  236.     Ok, e-mail me for more details on memory too then.
  237.  
  238. Ok.  Now, we know how to find the correct memory location.  How do we
  239. actually place a value there?  I'm so glad you asked that question,
  240. because it shows that you are paying attention.  So, moving steadily
  241. onward........
  242.  
  243.     PLACING A PIXEL!
  244.     ----------------
  245.  
  246.     As I mentioned above there are basically two ways to do this.  I will
  247.     code the first and explain it as I go along.  It is simpler because
  248.     we don't have to calculate the offset or fiddle with memory.
  249.  
  250.     Here we go.....
  251.  
  252.     // -------------------------------------
  253.     // PutIntPixel Function - Barny Mercer
  254.     // -------------------------------------
  255.  
  256.     void PutIntPixel( int X, int Y, int Colour )
  257.     {
  258.         _asm
  259.         {
  260.             mov ah,  0x0C       ; specify apropriate function
  261.             mov al,  [Colour]   ; put required colour in low byte
  262.             mov cx,       [X]   ; X coordinate
  263.             mov dx,       [Y]   ; Y coordinate
  264.             mov bx, 0x01        
  265.             int 10h             ; execute interrupt
  266.         }
  267.     }
  268.  
  269.     I've demonstrated this function in Intpixel.exe (source also included)
  270.  
  271.     The biggest drawback to this function is that it is soooo _s_l_o_w_
  272.     So now I will demonstrate the DMA principles that we examined earlier.
  273.  
  274.     Oh..!  One extra thing before I begin coding.
  275.     In order to access a specific memory segment we need to be able to
  276.     point to it in some way.  The way we do this is by using a pointer
  277.     to the correct segment. Like this....
  278.  
  279.     unsigned char *vga = (unsigned char *)0xA0000000;        
  280.  
  281.     Now the variable 'vga' can be used to reference our video memory.
  282.  
  283.     // -------------------------------------
  284.     // PutDMAPixel Function - Barny Mercer
  285.     // -------------------------------------
  286.  
  287.     void PutDMAPixel( int X, int Y, int Colour )
  288.     {
  289.         unsigned int Offset;
  290.  
  291.         Offset = (Y*320)+X;             // this could be done
  292.                            on-the-fly but I have used
  293.                            a variable for clarity
  294.  
  295.         // memset(vga+(Y*320)+X, Colour, 1);  // on-the-fly version
  296.  
  297.         memset(vga+Offset, Colour, 1);  // note that you could place
  298.                            many pixels by replacing
  299.                            the 1
  300.     }
  301.  
  302.     Ok.  This is demonstrated in 'dmapixel.exe' (source also included).
  303.  
  304.     It's still not very fast though.  
  305.     'What can we do to make it faster?' I hear you murmur through your
  306.     boredom induced haze, uncertain as to weather asking the question was
  307.     wise.
  308.  
  309.     I am afraid that in order to speed things up, we're going to have to
  310.     do a bit more assembly.  All set?  No?  Tough.....
  311.  
  312.     First, I will code the above routine in assembler and then we can
  313.     look at making it faster.  Ha-wun, Ha-two, Ha-wun, two, three, four..
  314.     Ahem...
  315.  
  316.     // -------------------------------------
  317.     // PutASMPixel Function - Barny Mercer
  318.     // -------------------------------------
  319.  
  320.     void PutASMPixel( int X, int Y, int Colour )
  321.     {
  322.         _asm
  323.         {
  324.             mov ax, 0xA000   ; point AX to video memory
  325.             mov es, ax       ; move segment pointer to ES
  326.                        (actual pointer)
  327.  
  328.             ; calculate offset
  329.             mov ax, [Y]      ; place Y value in AX
  330.             mul ax, 320      ; AX=Y*320
  331.             add ax, [X]      ; add X coordinate to AX
  332.                      ; now AX = (Y*320)+X, as per previous
  333.                      ; example
  334.  
  335.             ; move offset to offset pointer
  336.             mov di, ax
  337.  
  338.             mov es:[di], [Colour] ; move colour to memory
  339.         }
  340.     }
  341.  
  342.     Ok.  Looks complicated, but it isn't that bad really.  It is basically
  343.     the same as the previous one but written in assembly so that we can
  344.     see what needs doing to speed it up a bit.
  345.  
  346.     The first thing that I notice is the use of the MUL command.
  347.     A MUL is _very_ _very_ slow and this is what is slowing this
  348.     function down.  Sooooo.... let's remove it!
  349.     
  350.     'Please Sir.  How do we remove it and still find the correct offset?'
  351.     chirrups a small voice from the back of the class.
  352.  
  353.     If Y = 104 then Y*320 = 33280
  354.  
  355.     104 * 256 = 26624
  356.     104 *  64 =  6656
  357.           ---   -----
  358.           320   33280
  359.  
  360.     'What advantage is there to doing it in two stages?, you may well ask.
  361.     And that I will endevour to make clear. Take a look....
  362.  
  363.     SHL register, [value]
  364.  
  365.     The assembly SHL (SHift logical Left) command rotates a 16 bit value
  366.     stored in the register passed by the number of binary places in 
  367.     'value'.
  368.  
  369.     Looking at binary patters we can see the following:
  370.     
  371.     104 x 320 = 33280
  372.  
  373.     01101000 * 0000000101000000 = 0110100000000000  (26624)
  374.                     + 0001101000000000  ( 6656)
  375.                       ----------------
  376.                     = 1000001000000000 = 33280
  377.                       ----------------
  378.  
  379.     So by doing SHL ax, 8           ; ax = Y * 256
  380.             SHL bx, 6           ; bx = Y *  64
  381.             ADD ax, bx          ; ax = (Y*256)+(Y*64) = (Y*320)
  382.     
  383.     Instead of  MUL ax, 320         ; ax = (Y*320)
  384.     what have we gained?
  385.  
  386.     Take a look at the following table:
  387.  
  388.     Operation  Clocks (486)    1 clock = (1000/CPU speed Mhz) nanoseconds
  389.  
  390.     ADD        1
  391.     MOV        1
  392.     SHL        3
  393.     MUL        26!
  394.  
  395.     So as you can see, method 1 requires only 7 clocks to execute while
  396.     the MUL method requires 26 clock ticks.
  397.  
  398.     And there you have it!
  399.  
  400.     Oh....  There is a way to shave another 2 clocks off that multiply
  401.     routine.  Instead of doing (SHL ax, 8), a (MOV ah, al) will achieve
  402.     the same effect but 2 clocks faster.
  403.  
  404.     Here is the finished PutPixel function.
  405.  
  406.     void PutASMPixel( int X, int Y, unsigned char Colour )
  407.     {
  408.         _asm
  409.         {
  410.             mov ax, 0xA000   ; point AX to video memory
  411.             mov es, ax       ; move segment pointer to ES
  412.                      ; (actual pointer)
  413.             mov bx, [Y]
  414.             mov ax, bx       ; register to register is faster by 1 clock
  415.  
  416.             mov ah, al       ; ax=y*256 + y
  417.             mov al,  0       ; ax=y*256
  418.  
  419.             shl bx, 6        ; bx=y*64
  420.             add bx, ax       ; bx=y*320
  421.  
  422.             add bx, [X]      ; ax=(y*320)+x
  423.             mov di, bx       ; move video pointer to correct place
  424.  
  425.             mov al, [Colour]
  426.             mov es:[di], al  ; move colour to memory
  427.         }
  428.     }
  429.  
  430.     And there you go!  A nice fast assembly PutPixel routine.
  431.  
  432.     A lot of work?  Well yes, but only because of the need for speed in
  433.     a pixel routine.  A slow PutPixel will result in other routines which
  434.     require it to be slowed too.
  435.  
  436.     It is possible to create a faster PutPixel, but I'll leave you with
  437.     this one for now.
  438.  
  439. I hope that this tutorial has been helpful to you.  It is by no means perfect,
  440. but that doesn't make it worthless.
  441.  
  442. If there is anything in this tutorial which you would like further details on,
  443. then please do not hesitate to contact me.
  444.  
  445. If you produce anything nice, then please send it to me.  I am always keen
  446. to see other people's work.
  447.  
  448. Comments on this tutorial will be gratefully recieved.  Was it any use to you?
  449. Too informal?  Too complicated?  Not clear enough?  Too simple?
  450. What subjects would you like to see covered here?
  451.  
  452. -------------------------------------------------------------------------------
  453.  
  454. 'The true measure of a hero, is one who can lay down his life for others
  455.  in the knowledge that no one will ever know......'
  456.  
  457.  (Source unknown)
  458. ------------------------------------------------------------------------------
  459.  
  460. Barny Mercer    - (original code & text file) 29/7/95 @ 12:22am
  461.  
  462. email : barny.mercer@zetnet.co.uk
  463. WWW   : http://zetnet.co.uk/users/bmercer/
  464. voice : 01595 692097 (UK)
  465.  
  466. Richard Griffiths       - Pascal conversion
  467. email : richard.griffiths@zetnet.co.uk
  468. WWW   : http://zetnet.co.uk/users/rgriff/
  469.     
  470.